<?php
/**
 * Created by PhpStorm.
 * User: Qch
 * Date: 2017/7/2
 * Time: 15:19
 */

namespace Application\Index\Controller;


class ShuDu
{
    const ROWS     = 0;
    const COLUMNS  = 1;
    const BLOCKS   = 2;

    protected $rules = [[],[],[]];

    protected $values;

    protected $steps = [];

    /**
     * @var array 完成状态（防止重复动作）
     */
    protected $complete = [];

    /**
     * @var ShuDuCell[]
     */
    protected $cells = [];

    public function setValue($row, $column, $value)
    {
        $cell = $this->getCell($row, $column);
        $cell->setValue($value);

        //$this->complete[ShuDuCell::rc2p($row, $column)] = $value;

        return $cell;
    }

    public function analysis()
    {
        //1、求余动作
    }

    /**
     * 获取cell对象
     *
     * @param int $row 行号
     * @param int $column 列号
     *
     * @return ShuDuCell
     */
    public function getCell($row, $column)
    {
        $position = ShuDuCell::rc2p($row, $column);

        if(!isset($this->cells[$position])){

            $this->cells[$position] =  new ShuDuCell($row, $column, $this);
        }

        return $this->cells[$position];
    }

    /**
     * 获取规则单位
     * @param $row
     * @param int $rule
     *
     * @return ShuDuRow
     */
    public function getRule($row, $rule = self::ROWS)
    {
        if(!isset($this->rules[$rule][$row])){
            switch ($rule){
                case ShuDu::ROWS :
                    $this->rules[$rule][$row] = new ShuDuRow($this, $rule, $row);
                    break;
                case ShuDu::COLUMNS :
                    $this->rules[$rule][$row] = new ShuDuColumn($this, $rule, $row);
                    break;
                case ShuDu::BLOCKS :
                    $this->rules[$rule][$row] = new ShuDuBlock($this, $rule, $row);
                    break;
                default :
                    $this->rules[$rule][$row] = null;
            }
        }

        return $this->rules[$rule][$row];
    }

    public function addStep($row, $column, $value)
    {
        if($this->getCell($row, $column)->isComplete()) return;

        $this->steps[] = [$row, $column, $value];
        $this->setValue($row, $column, $value);
    }

    public function getStep($step = null)
    {
        if($step === null) return $this->steps;

        if(isset($this->steps[$step])) return $this->steps[$step];

        return [];
    }

    public function forStep($func, $from = 0, $to = -1)
    {
        while(isset($this->steps[$from])){
            $func($this->steps[$from], $from);

            $from++;

            // 主动跳出
            if($to !== -1 and $from > $to) break;
        }
    }

    public function dump()
    {
        $html = '';
        foreach ($this->rules as $rule){
            /** @var ShuDuRow $r */
            foreach ($rule as $r){
                $html .= $r->dump().'<br />';
            }
        }

        foreach ($this->cells as $cell){
            $html .= $cell->dump().'<br />';
        }


        return $html;
    }
}


class ShuDuRow extends ShuDuItem
{
    /**
     * @var ShuDu
     */
    protected $shuDu;

    protected $type;

    protected $id;

    protected $seats    = [0,1,2,3,4,5,6,7,8];

    public function __construct(ShuDu $shuDu, $type, $id)
    {
        $this->shuDu = $shuDu;
        $this->type  = $type;
        $this->id    = $id;
    }

    /**
     * 排除一个值
     *
     * @param int $value
     * @param int $seat
     *
     */
    public function exclude($value, $seat = -1)
    {
        // 排除过的值不重复排除
        if(!isset($this->requires[$value-1])) return ;

        unset($this->requires[$value-1]);
        unset($this->seats[$seat]);

        $this->checkOnly();

        $this->informRule($value);
    }

    public function must()
    {
        $shuDu = $this->shuDu;
        foreach ($this->requires as $require){
            $cell = null;
            if($this->forCells(function ($row ,$column) use ($shuDu, $require, &$cell){
                if($shuDu->getCell($row, $column)->can($require)){
                    if($cell !== null) return false;
                    $cell = [$row, $column];
                }
                return true;
            })) $this->shuDu->addStep($cell[0],$cell[1],$require);
        }
    }

    protected function checkOnly()
    {
        $only = $this->getOnly();
        if($only){
            reset($this->seats);
            $seat =  current($this->seats);
            $this->onOnly($seat, $only);
        }
    }

    protected function onOnly($seat, $only)
    {
        $this->shuDu->addStep($this->id,$seat,$only);
    }

    protected function informRule($value)
    {
        $shuDu = $this->shuDu;
        $this->forCells(function ($row ,$column) use ($shuDu,$value){
            $shuDu->getCell($row, $column)->exclude($value);
        });
    }

    public function dump()
    {
        if(empty($this->requires)) return '';

        $name = ['Row','Column','Block'];
        $html  = $name[$this->type].' '.$this->id.':';
        $html .= 'want('.implode(',',$this->requires).')';
        $html .= 'seat('.implode(',',$this->seats).')';

        return $html;

    }

    protected function forCells($func)
    {
        for ($j = 0; $j<9; $j++){
            if($func($this->id, $j) === false) return false;
        }
        return true;
    }
}

class ShuDuColumn extends ShuDuRow
{
    protected function onOnly($seat, $only)
    {
        $this->shuDu->addStep($seat,$this->id,$only);
    }

    protected function forCells($func)
    {
        for ($j = 0; $j<9; $j++){
            $func($j, $this->id);
        }
    }
}

class ShuDuBlock extends ShuDuRow
{
    protected function onOnly($seat, $only)
    {
        list($row,$column) = ShuDuCell::rcbp($this->id,$seat);
        $this->shuDu->addStep($row,$column,$only);
    }

    protected function forCells($func)
    {
        $block = $this->id;

        $b = ShuDuCell::m3($block);

        for($i = 0; $i < 3; $i++){
            $row = $b*3+$i;
            for($j = 0; $j < 3; $j++){
                $column = $block*3+$j;
                $func($row, $column);
            }
        }
    }
}

class ShuDuCell extends ShuDuItem
{
    /**
     * @var int 所在行
     */
    protected $row;
    /**
     * @var int 所在列
     */
    protected $column;
    /**
     * @var int 所在区块
     */
    protected $block;
    /**
     * @var int 所在区位
     */
    protected $place;

    /**
     * @var int 所在位置
     */
    protected $position;

    /**
     * @var ShuDu
     */
    protected $shuDu;

    /**
     * @var int 值
     */
    protected $value = 0;

    public function __construct($row, $column, $shuDu)
    {
        $this->shuDu = $shuDu;

        $this->row    = $row;
        $this->column = $column;

        $this->position = self::rc2p($row, $column);

        list($this->block,$this->place) = self::rcbp($row, $column);
    }

    /**
     * 设置值
     *
     * @param int $value 值
     * @return bool|null
     */
    public function setValue($value)
    {
        if(!$this->value){
            $this->value = $value;

            $this->requires = [];

            // 通知大家
            $this->shuDu->getRule($this->row, ShuDu::ROWS)->exclude($value, $this->column);
            $this->shuDu->getRule($this->column, ShuDu::COLUMNS)->exclude($value, $this->row);
            $this->shuDu->getRule($this->block, ShuDu::BLOCKS)->exclude($value, $this->place);

            // 求余动作
            $this->shuDu->getRule($this->row, ShuDu::ROWS)->must();
            $this->shuDu->getRule($this->column, ShuDu::COLUMNS)->must();
            $this->shuDu->getRule($this->block, ShuDu::BLOCKS)->must();

            return true;
        }
        if($this->value !== $value){
            return false;
        }
        return null;

    }

    public function exclude($value)
    {
        if($this->value or !isset($this->requires[$value-1]))  return;

        unset($this->requires[$value-1]);

        $only = $this->getOnly();
        if($only){
            $this->shuDu->addStep($this->row, $this->column, $only);
        }
    }

    public function can($value)
    {
        if($this->value) return $value === $this->value;

        return isset($this->requires[$value-1]);
    }

    public function getInfo()
    {
        return [$this->row,$this->column,$this->block,$this->place,$this->position,$this->value];
    }

    public function dump()
    {
        if($this->value) return '';
        $html  = "Cell({$this->position}):[".implode(',',$this->requires).']';
        return $html;
    }

    public static function rc2p($row, $column)
    {
        return $row * 9 + $column;
    }

    public static function rcbp($row, $column)
    {
        $r = self::m3($row);

        $c = self::m3($column);

        return [$r*3 + $c, $row*3 + $column];
    }

    public static function m3(&$v)
    {
        $v2 = 0;
        while ($v - 3 >= 0){
            $v2++;
            $v -= 3;
        }
        return $v2;
    }
}


abstract class ShuDuItem
{
    protected $requires = [1,2,3,4,5,6,7,8,9];

    /**
     * 所有的可能值
     * @return array
     */
    public function wantValues()
    {
        return $this->requires;
    }

    /**
     * 唯一值
     *
     * @return int
     */
    public function getOnly()
    {
        if(count($this->requires) !== 1) return 0;

        return reset($this->requires);
    }

    public function isComplete()
    {
        return empty($this->requires);
    }
}